<template>
	<div class="cesium-main">
		<div ref="container" class="cesium-container" />
		<div class="cesium-ui">
			<template v-if="ready">
				<slot />
			</template>
		</div>
	</div>
</template>

<script>
import { Viewer, ScreenSpaceEventHandler, ScreenSpaceEventType } from "cesium";
// import "@/assets/widgets.css";
import { Event } from "@/utils/eventbus";
import kebabCase from "lodash/kebabCase";
import throttle from "lodash/throttle";
export default {
	name: "CesiumViewer",
	props: {
		properties: {
			type: Object,
			default: () => {
				return Object.freeze({});
			},
		},
		preConfig: {
			type: Function,
			default: () => {},
		},
	},
	data() {
		return {
			ready: false,
		};
	},
	created() {
		this.eventBus = new Event();
	},
	mounted() {
		this.$nextTick(() => {
			this.createViewer();
		});
	},
	beforeDestroy() {
		this.removeViewer();
	},
	methods: {
		createViewer() {
			this.viewer = window.viewer = new Viewer(document.querySelector(".cesium-container"), this.properties);
			// 解决pickPosition拾取错误的问题
			this.viewer.scene.globe.depthTestAgainstTerrain = false;
			this.preConfig(this.viewer);
			this.attachEvents();
			this.ready = true;
			//   缩放最大高度
			//   this.viewer.scene.screenSpaceCameraController.minimumZoomDistance = 300;
		},
		removeViewer() {
			this.detachEvents();
			this?.viewer.destroy();
		},
		attachEvents() {
			this.attachViewerEvents();
			this.attachCameraEvents();
			this.attachSceneEvents();
		},
		detachEvents() {
			if (this.eventBus) {
				this.eventBus?.destroy();
			}
			this?.detachViewerEvents();
			this?.detachCameraEvents();
			this?.detachSceneEvents();
		},
		attachViewerEvents() {
			this.eventBus.entitys = [];
			if (this.handler) this.detachEvents();
			const handler = (this.handler = new ScreenSpaceEventHandler(this.$refs.container));
			const that = this;
			for (const key in ScreenSpaceEventType) {
				const value = ScreenSpaceEventType[key];
				handler.setInputAction(
					throttle(function (e) {
						that.handleEvent(e, kebabCase(key), value);
					}, 30),
					value
				);
			}
		},
		detachViewerEvents() {
			if (this.handler) {
				this.handler?.destroy();
			}
		},
		attachCameraEvents() {
			const { camera } = this.viewer.scene;
			const eventList = ["moveStart", "moveEnd", "changed"];
			const removeList = [];
			const that = this;
			eventList.forEach((event) => {
				removeList.push(
					camera[event].addEventListener(
						throttle(function () {
							that.handleCameraEvent(`camera-${kebabCase(event)}`);
						}, 30)
					)
				);
			});
			this.detachCameraEvents = () => {
				removeList.forEach((i) => i());
			};
		},
		attachSceneEvents() {
			const { scene } = this.viewer;
			const eventList = ["morphComplete", "morphStart", "postRender", "postUpdate", "preRender", "preUpdate", "terrainProviderChanged"];
			const removeList = [];
			eventList.forEach((event) => {
				removeList.push(
					scene[event].addEventListener(
						throttle(() => {
							this.handleSceneEvent(`scene-${kebabCase(event)}`);
						}, 30)
					)
				);
			});
			this.detachSceneEvents = () => {
				removeList.forEach((i) => i());
			};
		},
		handleEvent(e, eventName) {
			const mousePosition = e.position || e.endPosition;
			let position = this.pickPosition(mousePosition);
			let picked = this.pickObj(mousePosition, true);
			this.$emit(eventName, {
				...e,
				picked,
				pickedPosition: position,
			});
			picked?.forEach((i) => {
				const emits = [];

				if (i.id && i.id.$emit) emits.push(i.id.$emit);
				if (i.$emit) emits.push(i.$emit);
				emits.forEach((emit) => {
					emit(eventName, {
						...e,
						picked,
					});
				});
			});
		},
		handleCameraEvent(eventName) {
			const { camera } = this.viewer.scene;
			this?.eventBus?.emit(eventName, {
				eventType: eventName,
				event: camera,
			});
			this.$emit(eventName, camera);
		},
		handleSceneEvent(eventName) {
			const scene = this.viewer.scene;
			this?.eventBus?.emit(eventName, {
				eventType: eventName,
				event: scene,
			});
			this.$emit(eventName, scene);
		},
		removeEntity(entity) {
			try {
				this?.viewer.entities.remove(entity);
			} catch (error) {}
		},
		pickPosition(screenPosition) {
			return this?.viewer?.scene?.pickPosition(screenPosition);
		},
		pickObj(screenPosition, drill = false) {
			try {
				return drill ? this.viewer.scene.drillPick(screenPosition) : this.viewer.scene.pick(screenPosition);
			} catch (error) {}
			return undefined;
		},
	},
};
</script>

<style lang="scss">
@import "@/assets/widget.scss";
.cesium-main {
	position: relative;
	&::v-deep {
		.cesium-viewer-bottom,
		.cesium-viewer-animationContainer {
			display: none;
		}
	}
}
.cesium-viewer-bottom,
.cesium-viewer-animationContainer {
	display: none;
}
.cesium-main,
.cesium-container,
.cesium-viewer-cesiumWidgetContainer,
.cesium-viewer,
.cesium-ui {
	width: 100%;
	height: 100%;
	overflow: hidden;
}
.cesium-container {
	position: absolute;
	z-index: 1;
}
.cesium-ui {
	//   z-index: 2;
	position: absolute;
	top: 0px;
	left: 0px;
	pointer-events: none;
	& * {
		pointer-events: all;
	}
}
</style>
